package com.bigflexlib.mouse
{
	
	import com.bigflexlib.core.dependency.DependencySpec;
	import com.bigflexlib.x.flash.events.EnterFrameHelper;
	
	import flash.display.DisplayObject;
	import flash.display.Stage;
	import flash.events.Event;
	import flash.geom.Point;
	import flash.geom.Rectangle;

[Event(name="hoverChanged", type="com.bigflexlib.mouse.MouseHoverTrackerEvent")]	

[Event(name="strategyChanged", type="com.bigflexlib.mouse.MouseHoverTrackerEvent")]	
	
	
	public class MouseHoverTracker extends MouseTrackerBase
	{
		
		//////////////
		// strategies
		/////////////////////////
		
		public static const STRATEGY_HIT_TEST					:int = 1;
		public static const STRATEGY_HIT_TEST_SHAPE_FLAG		:int = 2;
		public static const STRATEGY_RECT						:int = 3;
		public static const STRATEGY_BOUNDS						:int = 4;
		
		/**
		 * Use width and height properties in conjunction with 
		 * matrix transformations.
		 * This will work even if the target is invisible
		 * or has no background color.
		 * 
		*/		
		public static const STRATEGY_WH						:int = 5;
		
		//////////////
		// events
		/////////////////////////
		
		// these could be removed because we have a dedicated event class
		// but we won't until we're certain they're not being used elsewhere
		// and that requires close inspection of all dependent projects
		// which means a big nono. Just consider deprecated
		public static const hoverChanged	:String = "hoverChanged";
		public static const strategyChanged	:String = "strategyChanged";
		
		
		///////////////////////////////////////
		// hover
		///////////////////////////////////////
		
		private var _hover:Boolean;
		[Bindable( hoverChanged )]
		public function get hover( ):Boolean
		{
			return _hover;
		}

		///////////////////////////////////////
		// strategy
		///////////////////////////////////////
		
		private var strategySpec:DependencySpec = new DependencySpec( 
			"strategy", 
			strategyChanged, 
			null, 
			false, 
			STRATEGY_HIT_TEST 
			);
			
		[Bindable( strategyChanged )]
		/**
		 * Default is HIT_TEST
		 * @return 
		 * 
		 */		
		public function get strategy( ):int
		{
			return dh.getDependencyValue( strategySpec );
		}
		public function set strategy( v:int ):void
		{
			dh.setDependencyValue( strategySpec, v );
		}
		
		
		///////////////////////////////////////
		// constructor
		///////////////////////////////////////
		
		public function MouseHoverTracker( )
		{
			EnterFrameHelper.i.addEventListener( Event.ENTER_FRAME, onEnterFrame, false, 0, true );
		}
		
		protected function onEnterFrame( event:Event ):void
		{
			update( );
		}
		
		override protected function dependenciesUpdated( ):void
		{
			update( );
		}
		
		private function update( ):void
		{
			if ( ! enabled )
				setNewHover( false );
			else 
				setNewHover( testHover( target, strategy ) );
		}
		
		/**
		 * 
		 * A satic way to access the various hit test strategies
		 * 
		 * @param target
		 * @param strategy
		 * @param pointOnStage
		 * @return 
		 * 
		 */
		public static function testHover( target:DisplayObject, strategy:int, pointOnStage:Point=null ):Boolean
		{
			try
			{
				var stage:Stage = target.stage;
				var p:Point = pointOnStage ? pointOnStage : new Point( stage.mouseX, stage.mouseY );
				var rect:Rectangle;
				switch ( strategy )
				{
					case STRATEGY_HIT_TEST :
						return target.hitTestPoint( p.x, p.y );
					break;

					case STRATEGY_HIT_TEST_SHAPE_FLAG : 
						return target.hitTestPoint( p.x, p.y, true );
					break;

					case STRATEGY_RECT :
						return target.getRect( target.stage ).containsPoint( p );
					break;
					
					case STRATEGY_BOUNDS : 
						return target.getBounds( target.stage ).containsPoint( p );
					break;

					case STRATEGY_WH : 
						return new Rectangle(	0, 0, target.width, target.height )
											.containsPoint( target.globalToLocal( p ) );
					break;
				}				
			} catch (e:Error){}
			
			return false; // default or error
		}
		
		
		private function setNewHover( v:Boolean ):void
		{
			if ( v == _hover )
				return;
			_hover = v;
			dispatchEvent( new MouseHoverTrackerEvent( MouseHoverTrackerEvent.HOVER_CHANGED ) );
		}
		
	}

}